{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "69ff6878-ffce-4d2a-b13a-951a06988704",
   "metadata": {},
   "source": [
    "因为上传文件以「表单数据」形式发送。\r\n",
    "\r\n",
    "所以接收上传文件，要预先安装 python-multipart。\r\n",
    "\r\n",
    "例如： pip install python-multip\n",
    "\n",
    "定义文件参数时使用 UploadFile：art。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "4c3bf9f9-b63d-45d0-ab7c-e5d103af0a10",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Started server process [23576]\n",
      "INFO:     Waiting for application startup.\n",
      "INFO:     Application startup complete.\n",
      "INFO:     Uvicorn running on http://0.0.0.0:8009 (Press CTRL+C to quit)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     127.0.0.1:63751 - \"POST /uploadfile/ HTTP/1.1\" 422 Unprocessable Entity\n",
      "INFO:     127.0.0.1:63759 - \"GET /docs HTTP/1.1\" 200 OK\n",
      "INFO:     127.0.0.1:63759 - \"GET /openapi.json HTTP/1.1\" 200 OK\n",
      "INFO:     127.0.0.1:63806 - \"POST /uploadfile/ HTTP/1.1\" 400 Bad Request\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Did not find boundary character 57 at index 4\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     127.0.0.1:64006 - \"POST /uploadfile/ HTTP/1.1\" 400 Bad Request\n",
      "INFO:     127.0.0.1:64026 - \"POST /uploadfile/ HTTP/1.1\" 200 OK\n",
      "INFO:     127.0.0.1:64070 - \"POST /uploadfile/ HTTP/1.1\" 400 Bad Request\n",
      "INFO:     127.0.0.1:64125 - \"POST /uploadfile/ HTTP/1.1\" 400 Bad Request\n",
      "INFO:     127.0.0.1:64229 - \"POST /uploadfile/ HTTP/1.1\" 400 Bad Request\n",
      "INFO:     127.0.0.1:51473 - \"POST /uploadfile/ HTTP/1.1\" 200 OK\n",
      "INFO:     127.0.0.1:51492 - \"POST /uploadfile/ HTTP/1.1\" 422 Unprocessable Entity\n",
      "INFO:     127.0.0.1:51501 - \"POST /uploadfile/ HTTP/1.1\" 200 OK\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Shutting down\n",
      "INFO:     Waiting for application shutdown.\n",
      "INFO:     Application shutdown complete.\n",
      "INFO:     Finished server process [23576]\n"
     ]
    }
   ],
   "source": [
    "import uvicorn\n",
    "from fastapi import FastAPI, UploadFile\n",
    "app = FastAPI()\n",
    "\n",
    "@app.post(\"/uploadfile/\")\n",
    "async def create_upload_file(file: UploadFile):\n",
    "    return {\"filename\": file.filename}\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    config = uvicorn.Config(app, host='0.0.0.0', port=8009)\n",
    "    server = uvicorn.Server(config)\n",
    "    await server.serve()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1fa7fde7-6255-4928-b8e7-9d9588ab611d",
   "metadata": {},
   "outputs": [],
   "source": [
    "url = \"http://127.0.0.1:8009/uploadfile/\"\n",
    "files = {\"file\": open('19.JPG', \"rb\")}\n",
    "res = requests.post(url, files=files)\n",
    "res.text\n",
    "\n",
    "# 先把一张图片放在和ipynb文件路径相同的地方，命名为 19.JPG\n",
    "# 在另一个ipynb文件中运行代码，会得到 '{\"filename\":\"19.JPG\"}'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30be6492-cd42-44aa-b282-d4936daa0db7",
   "metadata": {},
   "source": [
    "UploadFile 的属性如下：\r\n",
    "\r\n",
    "filename：上传文件名字符串（str），例如， myimage.jpg；\r\n",
    "content_type：内容类型（MIME 类型 / 媒体类型）字符串（str），例如，image/jpeg；\r\n",
    "file： SpooledTemporaryFile（ file-like 对象）。其实就是 Python文件，可直接传递给其他预期 file-like 对象的函数或\n",
    "\n",
    "UploadFile 支持以下 async 方法，（使用内部 SpooledTemporaryFile）可调用相应的文件方法。\r\n",
    "\r\n",
    "write(data)：把 data （str 或 bytes）写入文件；\r\n",
    "read(size)：按指定数量的字节或字符（size (int)）读取文件内容；\r\n",
    "seek(offset)：移动至文件 offset （int）字节处的位置；\r\n",
    "例如，await myfile.seek(0) 移动到文件开头；\r\n",
    "执行 await myfile.read() 后，需再次读取已读取内容时，这种方法特别好用；\r\n",
    "close()：关闭文件。\r\n",
    "因为上述方法都是 async 方法，要搭配「a\n",
    "使用 async 方法时，FastAPI 在线程池中执行文件方法，并 await 操作完成。\n",
    "\n",
    "例如，在 async 路径操作函数 内，要用以下方式读取文件内容：\n",
    "contents = await myfile.read()\n",
    "\n",
    "在普通 def 路径操作函数 内，则可以直接访问 UploadFile.file，例如：\n",
    "contents = myfile.file.read()\n",
    "\n",
    "wait」使用。支持库。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b25bdec2-9875-438c-bc6b-5ab7cbe74d13",
   "metadata": {},
   "source": [
    "## 可选文件上传"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d7e20a5-8bb4-4aac-8d9f-b82d1bd6e29a",
   "metadata": {},
   "source": [
    "您可以通过使用标准类型注解并将 None 作为默认值的方式将一个文件参数设为可选:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a5fbe80c-0a1d-4508-9bbb-5c9f7121be5e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import uvicorn\n",
    "from fastapi import FastAPI, UploadFile\n",
    "app = FastAPI()\n",
    "\n",
    "@app.post(\"/uploadfile/\")\n",
    "async def create_upload_file(file: UploadFile | None = None):\n",
    "    if not file:\n",
    "        return {\"message\": \"No upload file sent\"}\n",
    "    else:\n",
    "        return {\"filename\": file.filename}\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    config = uvicorn.Config(app, host='0.0.0.0', port=8009)\n",
    "    server = uvicorn.Server(config)\n",
    "    await server.serve()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e6a91e16-a532-45cd-a3d0-d0ff15e73768",
   "metadata": {},
   "source": [
    "## 多文件上传"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34b41093-0b46-4404-8fad-c3364f43b053",
   "metadata": {},
   "source": [
    "FastAPI 支持同时上传多个文件。\r\n",
    "\r\n",
    "可用同一个「表单字段」发送含多个文件的「表单数据」。\r\n",
    "\r\n",
    "上传多个文件时，要声明含 bytes 或 UploadFile 的列表（List）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "47670d11-7227-45d7-bd67-8ddbc02105e8",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Started server process [23576]\n",
      "INFO:     Waiting for application startup.\n",
      "INFO:     Application startup complete.\n",
      "INFO:     Uvicorn running on http://0.0.0.0:8009 (Press CTRL+C to quit)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     127.0.0.1:51826 - \"POST /uploadfiles/ HTTP/1.1\" 200 OK\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Shutting down\n",
      "INFO:     Waiting for application shutdown.\n",
      "INFO:     Application shutdown complete.\n",
      "INFO:     Finished server process [23576]\n"
     ]
    }
   ],
   "source": [
    "import uvicorn\n",
    "from fastapi import FastAPI, UploadFile\n",
    "app = FastAPI()\n",
    "\n",
    "@app.post(\"/uploadfiles/\")\n",
    "async def create_upload_files(files: list[UploadFile]):\n",
    "    return {\"filenames\": [file.filename for file in files]}\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    config = uvicorn.Config(app, host='0.0.0.0', port=8009)\n",
    "    server = uvicorn.Server(config)\n",
    "    await server.serve()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cf4dcf08-d233-4608-9e24-1a514b938584",
   "metadata": {},
   "outputs": [],
   "source": [
    "url = \"http://127.0.0.1:8009/uploadfiles/\"\n",
    "files = [(\"files\", open('19.JPG', \"rb\")),(\"files\", open('89.JPG', \"rb\"))]\n",
    "res = requests.post(url, files=files)\n",
    "res.text\n",
    "\n",
    "# 先把两张图片放在和ipynb文件路径相同的地方，命名为 19.JPG 和 89.JPG\n",
    "# 在另一个ipynb文件中运行代码，会得到 '{\"filenames\":[\"19.JPG\",\"89.JPG\"]}'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b3c7b91-0564-4999-8e10-2deadc293a3e",
   "metadata": {},
   "source": [
    "## 带前端测试页面"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "94d7e9b8-581d-4433-aa72-40d6b19e494c",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:     Started server process [23576]\n",
      "INFO:     Waiting for application startup.\n",
      "INFO:     Application startup complete.\n",
      "INFO:     Uvicorn running on http://0.0.0.0:8009 (Press CTRL+C to quit)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:     127.0.0.1:51896 - \"GET / HTTP/1.1\" 200 OK\n",
      "INFO:     127.0.0.1:51895 - \"POST /uploadfiles/ HTTP/1.1\" 200 OK\n"
     ]
    }
   ],
   "source": [
    "import uvicorn\n",
    "from fastapi import FastAPI, UploadFile\n",
    "from fastapi.responses import HTMLResponse\n",
    "app = FastAPI()\n",
    "\n",
    "@app.post(\"/uploadfiles/\")\n",
    "async def create_upload_files(files: list[UploadFile]):\n",
    "    return {\"filenames\": [file.filename for file in files]}\n",
    "\n",
    "@app.get(\"/\")\n",
    "async def main():\n",
    "    content = \"\"\"\n",
    "<body>\n",
    "<form action=\"/uploadfiles/\" enctype=\"multipart/form-data\" method=\"post\">\n",
    "<input name=\"files\" type=\"file\" multiple>\n",
    "<input type=\"submit\">\n",
    "</form>\n",
    "</body>\n",
    "    \"\"\"\n",
    "    return HTMLResponse(content=content)\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    config = uvicorn.Config(app, host='0.0.0.0', port=8009)\n",
    "    server = uvicorn.Server(config)\n",
    "    await server.serve()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "681fa3f0-248a-47a3-bf56-280b7337310b",
   "metadata": {},
   "source": [
    "打开浏览器 http://127.0.0.1:8009/ 可以上传文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "92570c0d-68d6-4c94-879a-4a498f795701",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
